/* telnetConnectionHandler.c */

#include "telnetConnectionHandler.h"
#include "httpConnectionHandler.h"
#include "socketHelper.h"
#include "fileHandler.h"
#include <stdio.h>
#include "configFile.h"
#include "utilities.h"

/* Defines */
#define COMMAND_BUFFER 5
#define TELNET_BUFFER_SIZE 256
#define AUTHORIZED 1337
#define NOT_AUTHORIZED 0

/* Prototypes */
static char* allocateTelnetBuffer(char* buffer, int size);
static void handleTelnetMessage(telnetConnectionT *conn);
static void telnetRecieveLoop(telnetConnectionT *conn);
static char* initTelnetBuffer();
static void authorizeUser(telnetConnectionT *conn, char* password);
static void isActive();
static void serverCommand(char *value, telnetConnectionT *conn);
static void getLogFile(telnetConnectionT* conn);


/* Function: telnetListenLoop
* Usage: telnetListenLoop();
* -------------------------------- 
* Listens for new telnet connections, if a new connection is recieved send it to the handleConnection function.
*/
void telnetListenLoop() {
	int listenInfoLength;
	SOCKET listenSocket;
	SOCKADDR_IN listenInfo;
	telnetConnectionT telnetConnection;

	initListenSocket(&listenInfo, &listenSocket, getTelnetPort());
	listenInfoLength = sizeof (listenInfo);
	while (1){
		telnetConnection = initNewTelnetConnection();
		printf("Waiting for new telnet connection...\n");
		telnetConnection.clientSocket = accept(listenSocket, (SOCKADDR*)&listenInfo, &listenInfoLength);

		handleTelnetConnection(&telnetConnection);
	}
}

/* Function: initNewTelnetConnection
* Usage: telnetConnection = initNewTelnetConnection();
* -------------------------------- 
* This function initializes the new telnet connection by givin values to some variables in a struct. Afterwards it returns
* the struct.
*/ 
telnetConnectionT initNewTelnetConnection() {
	telnetConnectionT newConnection;

	newConnection.buffer = initTelnetBuffer();
	newConnection.bufferSize = 0;
	newConnection.isAuthorized = NOT_AUTHORIZED;
	newConnection.clientSocket = INVALID_SOCKET;
	newConnection.isActive = INACTIVE;

	return newConnection;
}

/* Function: handleTelnetConnection
* Usage: handleTelnetConnection(conn);
* -------------------------------- 
* This function handles the telnet connection.
*/ 
void handleTelnetConnection(telnetConnectionT *conn) {
	printf("Handling telnet connection\n");
	send(conn->clientSocket, "Connection established.\r\n", sizeof("Connection established.\r\n"), 0);
	conn->isActive = ACTIVE;
	do {
		telnetRecieveLoop(conn);
		if (conn->isActive == INACTIVE)
			break;
		else {
			handleTelnetMessage(conn);
		}
	} while (conn->isActive == ACTIVE);

	shutdown(conn->clientSocket, SD_BOTH);
	closesocket(conn->clientSocket);
}

/* Function: handleTelnetMessage
* Usage: handleTelnetMessage(conn);
* -------------------------------- 
* Handles the message sent by a telnet client.
*/ 
static void handleTelnetMessage(telnetConnectionT *conn) {
	char *message, *command, *value;
	int ok, i;
	if (conn->bufferSize != 0) {
		message = initCharPointer(conn->bufferSize);
		memcpy(message, conn->buffer, conn->bufferSize);
		message[conn->bufferSize] = 0;

		for (i = 0; i < conn->bufferSize; i++) {
			if (conn->buffer[i] == ' ') break;
		}

		command = initCharPointer(i);
		value = initCharPointer(conn->bufferSize - (i+1));

		ok = sscanf(message, "%s %s", command, value);

		if (conn->isAuthorized == AUTHORIZED) {
			if (!strcmp(command, "ISACTIVE"))
				isActive(conn);
			else if (!strcmp(command, "GETLOGFILE"))
				getLogFile(conn);
			else if (!strcmp(command, "SERVER"))
				serverCommand(value, conn);
			else {
				printf("UNKNOWN COMMAND\n");
				send(conn->clientSocket, "UNKNOWN COMMAND\r\n", sizeof("UNKNOWN COMMAND\r\n"), 0);
			}
		}
		else {
			if (!strcmp(command, "AUTHORIZE"))
				authorizeUser(conn, value);
			else {
				printf("Not Authorized.\n");
				send(conn->clientSocket, "You are not authorized yet.\r\n", sizeof("You are not authorized yet.\r\n"), 0);
			}
		}
		free(message);
		free(value);
		free(command);
	}
}

/* Function: serverCommand
* Usage: serverCommand(value, conn);
* -------------------------------- 
* Starts and stops the HTTP server.
*/ 
static void serverCommand(char *value, telnetConnectionT *conn) {
	if (!strcmp(value, "START")) {
		resumeHttpServer(conn);
	}
	else if (!strcmp(value, "STOP")) {
		stopHttpServer(conn);
	}
}

/* Function: isActive
* Usage: isActive(conn);
* -------------------------------- 
* Checks if the server is running and sends status to a telnet client.
*/ 
static void isActive(telnetConnectionT *conn) {
	if (getHttpStatus() == HTTP_LISTEN_ON) {
		printf("Server is active.\n");
		send(conn->clientSocket, "The HTTP server is up and running.\r\n", sizeof("The HTTP server is up and running.\r\n"), 0);
	}
	else {
		printf("Server is inactive.\n");
		send(conn->clientSocket, "The HTTP server is down for incoming traffic.\r\n", sizeof("The HTTP server is down for incoming traffic.\r\n"), 0);
	}
}

/* Function: getLogFile
* Usage: getLogFile(conn);
* -------------------------------- 
* This function reads the log-file and if the file is not found, or if the file is empty it will say that the log file is empty.
* Else if it isn't empty it will send the log file to the telnet client. If nothing of these two happen it will print that there
* was an error reading the log-file.
*/ 
static void getLogFile(telnetConnectionT* conn) {
	fileInfoT fileInfo;
	int result;

	fileInfo.fileContent = NULL;
	result = readLogFile(&fileInfo);
	
	if (result == FILE_NOT_FOUND || fileInfo.fileContent == NULL || !strcmp(fileInfo.fileContent, "\n")){
		printf("The log file is empty.\n");
		send(conn->clientSocket, "The log file is empty.\r\n", sizeof("The log file is empty.\r\n"), 0);
	}
	else if (result == RESULT_OK) {
		printf("Sending log file to telnet client.\n%s\n", fileInfo.fileContent);
		send(conn->clientSocket, fileInfo.fileContent, fileInfo.contentSize, 0);
		send(conn->clientSocket, "\r\n", sizeof("\r\n"), 0);
	}
	else {
		printf("Reading log file went wrong\n");
	}
	free(fileInfo.fileContent);
}

/* Function: authorizeUser
* Usage: authorizeUser(conn, value);
* -------------------------------- 
* This function checks if the password it received as parameter is correct. If it is equal to the one in the config-file, the user
* will be authorize by setting the variable conn->isAuthorized to "AUTRHORIZED". It will also send the text "authorized to the telnet
* client. If the password not match the user will not be authorized and it will send a text that says "failed to authorize to the telnet client.
*/ 
static void authorizeUser(telnetConnectionT *conn, char* password) {
	if(!strcmp(password, getPassword())) {
		conn->isAuthorized = AUTHORIZED;
		printf("Authorized with password: %s\n", password);
		send(conn->clientSocket, "Authorized.\r\n", sizeof("Authorized.\r\n"), 0);
	}
	else {
		conn->isAuthorized = NOT_AUTHORIZED;
		printf("Failed to authorize with password: %s\n", password);
		send(conn->clientSocket, "Failed to authorize. Invalid password.\r\n", sizeof("Failed to authorize. Invalid password.\r\n"), 0);
	}
}

/* Function: telnetRecieveLoop
* Usage: telnetRecieveLoop(conn);
* -------------------------------- 
* Recieve data from a telnetclient also detects if a user disconnects.
*/ 
static void telnetRecieveLoop(telnetConnectionT *conn) {
	int dataSize, i = 0, bufferSize;
	char *buffer, ch = 0;

	buffer = initCharPointer(COMMAND_BUFFER);
	bufferSize = COMMAND_BUFFER;

	while (1) {
		dataSize = recv(conn->clientSocket, &ch, 1, 0);
		/* if connection closes */
		if (dataSize == 0) {
			conn->isActive = INACTIVE;
			break;
		}
		if (ch == '\n') {
			break;
		}
		if (ch > 0 && ch != '\r' && (isalnum(ch) || isspace(ch))) {
			if(!(isspace(ch) && i == 0)) {
				buffer[i] = ch;
				i++;
				if (i == bufferSize) {
					expandCharPointer(bufferSize + COMMAND_BUFFER, buffer);
					bufferSize += COMMAND_BUFFER;
				}
			}
		}
	}
	buffer[i] = 0;
	conn->buffer = allocateTelnetBuffer(conn->buffer, i+1);
	memset(conn->buffer, 0, i+1);
	memcpy(conn->buffer, buffer, i+1);
	conn->bufferSize = i;
	
	free(buffer);
	buffer = NULL;
}

/* Function: initTelnetBuffer
* Usage: newConnection.buffer = initTelnetBuffer();
* -------------------------------- 
* This function initializes the telnetBuffer by allocating memory for the buffer and returning it.
*/ 
static char* initTelnetBuffer() {
	char* buffer;
	
	buffer = (char*)malloc(TELNET_BUFFER_SIZE * sizeof(char));

	return buffer;
}

static char* allocateTelnetBuffer(char* buffer, int size) {
	buffer = (char*) realloc(buffer, size * sizeof(char));

	return buffer;
}